#include <tchar.h>

#include <boost/scoped_array.hpp>

#include "RegistryHelper.h"

#define REG_TREE_ABPA_BASE_HKEY		HKEY_LOCAL_MACHINE
#define REG_TREE_ABPA_BASE_PATH		_T("SOFTWARE\\Huji\\Abpa\\DataCollector")

#pragma warning (disable: 4127)  // there are constant conditional statements (they depend on template parameters)

namespace platform {

RegistryHelper *		RegistryHelper::_instance_ptr = NULL;
boost::once_flag		RegistryHelper::_instance_flag;



template<bool FORCE>
HKEY RegistryHelper::GetKey(HKEY base, LPCTSTR path) {
	HKEY key;
	LONG res;

	if (!FORCE) {
		res = ::RegOpenKeyEx(base,
							 path,
							 0,
							 KEY_READ | KEY_WRITE,
							 &key);
	} else {
		res = ::RegCreateKeyEx(base,
							   path,
							   0,
							   NULL,
							   REG_OPTION_NON_VOLATILE,
							   KEY_READ | KEY_WRITE,
							   NULL,
							   &key,
							   NULL);
	}

	if (res != ERROR_SUCCESS)
		THROW(Exception, "Failed to get/create registry key");

	return key;
}

RegistryHelper::RegistryHelper(void)
: _appBaseKey(GetKey<true>(REG_TREE_ABPA_BASE_HKEY, REG_TREE_ABPA_BASE_PATH))
{}

RegistryHelper::~RegistryHelper(void)
{}



///////////////////////////////////////
//
// RegistryKey class
//
///////////////////////////////////////


RegistryHelper::RegistryKey::RegistryKey(const RootKeys root, const XString &path)
: _key(GetKey<true>(Root2HKey(root), path.getTStr()))
{}


RegistryHelper::RegistryKey::RegistryKey(RegistryKey &key)
: _key(key._key)
{ key._key = NULL; }


RegistryHelper::RegistryKey::~RegistryKey(void) {
	if (_key != NULL)
		::RegCloseKey (_key);
}


__int32 RegistryHelper::RegistryKey::getInt32Value(const XString &name) const {
	if (_key == NULL)
		THROW(Exception, "using uninitialized RegistryKey");

	__int32 ret;

	DWORD type;
	DWORD size = sizeof(__int32);
	LONG res = ::RegQueryValueEx(_key,
								 name.getTStr(),
								 NULL,
								 &type,
								 reinterpret_cast<LPBYTE>(&ret),
								 &size);
	if (res != ERROR_SUCCESS)
		THROW(Exception, "could not find specified value");

	if (type != REG_DWORD)
		THROW(Exception, "the value is not int32");

	return ret;
}


void RegistryHelper::RegistryKey::getStringValue(const XString &name, XString &value) const {
	if (_key == NULL)
		THROW(Exception, "using uninitialized RegistryKey");

	DWORD type;
	TCHAR sbuf[1024];
	DWORD size = 1024;
	LONG res = ::RegQueryValueEx(_key,
								 name.getTStr(),
								 NULL,
								 &type,
								 reinterpret_cast<LPBYTE>(sbuf),
								 &size);

	if (type != REG_SZ && type != REG_EXPAND_SZ)
		THROW(Exception, "the value is not string");

	if (res == ERROR_SUCCESS) {
		value = sbuf;
		return;
	}

	if (res != ERROR_MORE_DATA)
		THROW(Exception, "failed to read the value");

	boost::scoped_array<TCHAR> buf(new TCHAR[size]);
	res = ::RegQueryValueEx(_key,
							name.getTStr(),
							NULL,
							&type,
							reinterpret_cast<LPBYTE>(buf.get()),
							&size);

	if (res != ERROR_MORE_DATA)
		THROW(Exception, "failed to read the value");

	value = buf.get();
}

void RegistryHelper::RegistryKey::setValue(const XString &name, __int32 val) {
	if (_key == NULL)
		THROW(Exception, "using uninitialized RegistryKey");

	LONG res = ::RegSetValueEx(_key,
							   name.getTStr(),
							   0,
							   REG_DWORD,
							   reinterpret_cast<BYTE*>(&val),
							   sizeof(__int32));
	if (res != ERROR_SUCCESS)
		THROW(Exception, "failed to set int32 value");
}

void RegistryHelper::RegistryKey::setValue(const XString &name, const XString &val) {
	if (_key == NULL)
		THROW(Exception, "using uninitialized RegistryKey");

	LONG res = ::RegSetValueEx(_key,
							   name.getTStr(),
							   0,
							   REG_SZ,
							   reinterpret_cast<const BYTE*>(val.getTStr()),
							   (static_cast<DWORD>(val.length()) + 1) * sizeof(TCHAR));
	if (res != ERROR_SUCCESS)
		THROW(Exception, "failed to set string value");
}


} // end namespace
